package org.example.se.io;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.springframework.util.Assert;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * IO 流转换工具类
 * 1、项目中经常需要对输入-输出流进行相互转换，比如：poi 将 word 文件写到输出流，然后压缩工具类需要一个输入流进行压缩，此时就需要进行转换
 * 2、ByteArrayInputStream、ByteArrayOutputStream：字节数组输入、输出流：
 * * 2.0、ByteArrayInputStream 包含一个内部缓冲区，其中包含可从流中读取的字节
 * * 2.1、ByteArrayOutputStream 其中数据被写入字节数组，当数据写入缓冲区时，缓冲区会自动增长。
 * * 2.2、ByteArrayOutputStream 可以使用 toByteArray() 和 toString() 检索数据。
 * * 2.3、类中的方法可以在流关闭后调用，而不会生成 IOException 异常。
 * 3、ByteArrayInputStream、ByteArrayOutputStream 分别继承自 InputStream 和 OutputStream。
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2021/11/10 19:57
 */
public class StreamConvertUtil {

    /**
     * InputStream 输入流转 ByteArrayOutputStream 字节数组输出流
     * 1、之所以输入流选择字节数组输入流，是因为它有公开的无参构造器，其它的类似 FileOutputStream 构造器都需要传入参数
     *
     * @param inputStream ：字节输入流
     * @return
     * @throws Exception
     */
    public static ByteArrayOutputStream inputStream2ByteArrayOutputStream(final InputStream inputStream) throws IOException {
        Assert.notNull(inputStream, "输入流 InputStream 不能为 null！");
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        //每次读取的大小
        int size;
        //每次读取的数据
        byte[] bytes = new byte[2048];
        while ((size = inputStream.read(bytes)) != -1) {
            //将每次读取的数据写入到输出流
            byteArrayOutputStream.write(bytes, 0, size);
        }
        return byteArrayOutputStream;
    }

    /**
     * ByteArrayOutputStream 字节数组输出流转为 ByteArrayInputStream 字节数组输入流
     *
     * @param byteArrayOutputStream ：字节数组输出流
     * @return
     */
    public static ByteArrayInputStream byteArrayOutputStream2ByteArrayInputStream(final ByteArrayOutputStream byteArrayOutputStream) {
        Assert.notNull(byteArrayOutputStream, "输出流 ByteArrayOutputStream 不能为 null！");
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return byteArrayInputStream;
    }

    /**
     * 将字节输入流转为字符串
     *
     * @param inputStream ：字节输入流
     * @param charsetName ：字符编码，比如 utf-8、gbk，可以为空.
     * @return
     * @throws Exception
     */
    public static String inputStream2String(final InputStream inputStream, String charsetName) throws Exception {
        Assert.notNull(inputStream, "输入流 inputStream 不能为 null！");
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 每次读取的字节大小
        int size;
        // 存储每次读取的字节内容
        byte[] bytes = new byte[2048];
        while ((size = inputStream.read(bytes)) != -1) {
            //写入到字节数组输出流
            byteArrayOutputStream.write(bytes, 0, size);
        }
        //String toString(String charsetName)：将缓冲区的内容转换为字符串，并指定编码对字节进行解码，
        String text = StringUtils.isBlank(charsetName) ? byteArrayOutputStream.toString() : byteArrayOutputStream.toString(charsetName);
        return text;
    }

    /**
     * 字符串转字节数组输入流
     *
     * @param source      ：源字符串内容
     * @param charsetName ：字符编码，比如 utf-8、gbk，可以为空.
     * @param
     * @return
     */
    public static ByteArrayInputStream string2ByteArrayInputStream(String source, String charsetName) throws UnsupportedEncodingException {
        Assert.hasText(source, "源字符串内容不能为空！");
        byte[] bytes = StringUtils.isBlank(charsetName) ? source.getBytes() : source.getBytes(charsetName);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        return byteArrayInputStream;
    }

    /**
     * 字符串转字节数组输出流
     *
     * @param source      ：源字符串内容
     * @param charsetName ：字符编码，比如 utf-8、gbk，可以为空.
     * @return
     * @throws Exception
     */
    public static ByteArrayOutputStream string2ByteArrayOutputStream(String source, String charsetName) throws Exception {
        Assert.hasText(source, "源字符串内容不能为空！");
        byte[] bytes = StringUtils.isBlank(charsetName) ? source.getBytes() : source.getBytes(charsetName);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(bytes.length);
        byteArrayOutputStream.write(bytes, 0, bytes.length);
        byteArrayOutputStream.flush();
        return byteArrayOutputStream;
    }

    //-----------------------------------------方法测试-------------------------------------

    @Test
    public void testInputStream2ByteArrayOutputStream() {
        // 类路径下的源文件
        String classPath = "data/images/1.jpg";
        // 生成的目标文件路径
        String destPath = "D:/" + System.currentTimeMillis() + classPath.substring(classPath.lastIndexOf("."));
        InputStream inputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            inputStream = StreamConvertUtil.class.getClassLoader().getResourceAsStream(classPath);
            byteArrayOutputStream = inputStream2ByteArrayOutputStream(inputStream);
            Files.write(Paths.get(destPath), byteArrayOutputStream.toByteArray());
            System.out.println("输出文件：" + destPath);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.flush();
                    byteArrayOutputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void testByteArrayOutputStream2ByteArrayInputStream() {
        // 类路径下的源文件
        String classPath = "data/config.properties";
        InputStream inputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        try {
            inputStream = StreamConvertUtil.class.getClassLoader().getResourceAsStream(classPath);
            byteArrayOutputStream = inputStream2ByteArrayOutputStream(inputStream);
            byteArrayInputStream = byteArrayOutputStream2ByteArrayInputStream(byteArrayOutputStream);

            //将循环读取的字节数据汇总到一个字节数组中，这样就不会出现转字符串时不完整的情况
            byte[] finalBytes = {};
            //每次读取的字节大小
            int size;
            //存放每次读取的字节数据
            byte[] bytes = new byte[1024];
            while ((size = byteArrayInputStream.read(bytes)) >= 0) {
                //org.apache.commons.lang3.ArrayUtils 提供了实用的方法.
                byte[] subarray = ArrayUtils.subarray(bytes, 0, size);
                finalBytes = ArrayUtils.addAll(finalBytes, subarray);
            }
            System.out.println("读取文件内容：\n" + new String(finalBytes, "gbk"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (byteArrayInputStream != null) {
                    byteArrayInputStream.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.flush();
                    byteArrayOutputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void testInputStream2String() {
        // 类路径下的源文件
        String classPath = "data/config.properties";
        InputStream inputStream = null;
        try {
            inputStream = StreamConvertUtil.class.getClassLoader().getResourceAsStream(classPath);
            String string = inputStream2String(inputStream, "gbk");
            System.out.println("读取文件内容：\n" + string);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void testString2ByteArrayInputStream() {
        // 类路径下的源文件
        String classPath = "data/config.properties";
        InputStream inputStream = null;
        try {
            inputStream = StreamConvertUtil.class.getClassLoader().getResourceAsStream(classPath);
            String string = inputStream2String(inputStream, "gbk");

            ByteArrayInputStream byteArrayInputStream = string2ByteArrayInputStream(string, "");
            String string1 = inputStream2String(byteArrayInputStream, "");
            System.out.println("读取文件内容：\n" + string1);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void testString2ByteArrayOutputStream() {
        // 类路径下的源文件
        String classPath = "data/config.properties";
        InputStream inputStream = null;
        try {
            inputStream = StreamConvertUtil.class.getClassLoader().getResourceAsStream(classPath);
            String string = inputStream2String(inputStream, "gbk");

            ByteArrayOutputStream byteArrayOutputStream = string2ByteArrayOutputStream(string, "");
            System.out.println("读取文件内容：\n" + byteArrayOutputStream.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}
